//
//  UUIDUtils.swift
//  
//
//  Created by Ink on 2024/2/16.
//

import Foundation

public class UUIDUtils: NSObject {
    
    private static var needResetUUID: Bool = false
    // Keychain > UserDefault > Pasteboard
    
    public static var saveKeychain = true
    public static var saveUserDefault = true
    public static var savePasteboard = true
    
    public static var UDIDBundleIdKey      = "LQUDID_BundleId"
    public static var UDIDKey  = "UDIDUserDeviceIdentifierKey"
    
    public static var UDIDSharedNameKey    = "com.lqudid.shared"
    public static var UDIDDataSaveKey      = "LQUDID_DataSaveKey"
    
    static var dictCache: Dictionary<String, Any>?
    
    @UserDefault(suiteName:UDIDSharedNameKey, UDIDDataSaveKey)
    static var dataUserDefaultCache: Data?
    
    @discardableResult
    public class func resetUDID () -> String {
        needResetUUID = true
        return getUDID()
    }
   
    public class func getUDID () -> String {
        
        if needResetUUID != true {
            if let uuid = dictCache?[UDIDKey] as? String{
                return uuid
            }
            if let result = getDict(), result.count > 0,let uuid = result[UDIDKey] as? String {
                dictCache = result
                saveDict(dict: result)
                return uuid
            }
        }
        
        let UDIDString = needResetUUID == true ? UUID().uuidString : generateFreshUDID
        needResetUUID = false
        let tempDictCache = [
            UDIDBundleIdKey : Bundle.main.bundleIdentifier as Any,
            UDIDKey : UDIDString
        ]
        dictCache = tempDictCache
        saveDict(dict: tempDictCache)
        return UDIDString
    }
    
    // MARK: private method
    private class var generateKeychainQuery: Dictionary<String, Any> {
        var keychainQuery: Dictionary<String, Any> = Dictionary.init()
        keychainQuery.updateValue(kSecClassGenericPassword, forKey: kSecClass as String)
        keychainQuery.updateValue(UDIDSharedNameKey, forKey: kSecAttrService as String)
        keychainQuery.updateValue(UDIDDataSaveKey, forKey: kSecAttrAccount as String)
        keychainQuery.updateValue(kSecAttrAccessibleAfterFirstUnlock, forKey: kSecAttrAccessible as String)
        return keychainQuery
    }
    
    private class var generateFreshUDID: String {
        let IDFV = UIDevice.current.identifierForVendor?.uuidString
        var prefixStr = ""
        if let UDID32 = IDFV?.replacingOccurrences(of: "-", with: "") {
            var i: Int = 1
            for str in UDID32 {
                if i%4==0 {
                    prefixStr.append(str)
                }
                i+=1
            }
            prefixStr+=UDID32
        }
        return prefixStr.lowercased()
    }
}

// MARK: read Data from cache
extension UUIDUtils {
    
    private class func getDict() -> Dictionary<String, Any>? {
        if let data = getDataFromKeychain() ?? getDataFromUserDefault() ?? getDataFromPasteboard() {
            if let dict = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Dictionary<String, Any> {
                return dict
            }
        }
        return nil
    }
    
    private class func getDataFromKeychain() -> Data? {
        var keychainQuery = generateKeychainQuery
        if let value = kCFBooleanTrue {
            keychainQuery.updateValue(value, forKey: kSecReturnData as String)
        }
        keychainQuery.updateValue(kSecMatchLimitOne, forKey: kSecMatchLimit as String)
        var queryResult: AnyObject?
        let _ = withUnsafeMutablePointer(to: &queryResult) {
            SecItemCopyMatching(keychainQuery as CFDictionary, UnsafeMutablePointer($0))
        }
        return queryResult as? Data
    }
    
    private class func getDataFromUserDefault () -> Data? {
        return dataUserDefaultCache
    }
    
    private class func getDataFromPasteboard() -> Data? {
        let pasteboard = UIPasteboard.init(name: UIPasteboard.Name(rawValue: UDIDSharedNameKey), create: false);
        return pasteboard?.data(forPasteboardType: UDIDDataSaveKey)
    }
}

// MARK: save Data to cache
extension UUIDUtils {
    
    private class func saveDict(dict: Dictionary<String, Any>) {
        
        var data:Data?
        
        guard !dict.isEmpty else { return }
        
        if #available(iOS 11, *) {
             data = try? NSKeyedArchiver.archivedData(withRootObject: dict, requiringSecureCoding: false)
        } else {
            data = NSKeyedArchiver.archivedData(withRootObject: dict)
        }
        
        if let data = data {
            saveDataToUserDefault(data: data)
            saveDataToKeychain(data: data)
            saveDataToPasteboard(data: data)
        }
    }
    
    private class func saveDataToKeychain (data: Data) {
        guard saveKeychain == true else { return }
        var keychainQuery = generateKeychainQuery
        SecItemDelete(keychainQuery as CFDictionary)
        keychainQuery.updateValue(data, forKey: kSecValueData as String)
        SecItemAdd(keychainQuery as CFDictionary, nil)
    }
    
    private class func saveDataToPasteboard (data: Data) {
        guard savePasteboard == true else { return }
        UIPasteboard(name: UIPasteboard.Name(rawValue: UDIDSharedNameKey), create: true)?
        .setData(data as Data, forPasteboardType: UDIDDataSaveKey)
    }
    
    private class func saveDataToUserDefault (data: Data) {
        guard saveUserDefault == true else { return }
        dataUserDefaultCache = data
    }
}
